#include "Client.h"
#include <sstream>
//#include <locale>
//#include <iomanip>


int main(int argc, char ** argv)
{
		char * ipAddress = "127.0.0.1";
	int port = 3000;
	bool debug = false;

	int option;
	while((option = getopt(argc, argv, "s:p:d:")) != -1)
	{
		switch (option)
		{
			case 'p':
				port = atoi(optarg);
				break;
			case 's':
				ipAddress = optarg;
				break;
			case 'd':
				debug = true;
				break;
			default:
				cout << option;
				cout << "' is an invalid option" << endl;
				cout << "USAGE: msg [-s IP address] [-p port] [-d DEBUG]" << endl;
				exit(EXIT_FAILURE);
		}
	}

	Client client(ipAddress, port, debug);
	
	client.connectServer();
	client.launchUI();
}

Client::Client(char * ipAddr, int port)
{
	init(ipAddr, port, false);
}

Client::Client(char * ipAddr, int port, bool debug)
{
	init(ipAddr, port, debug);
}

void Client::init(char * ipAddr, int port, bool d)
{
	ipAddress = ipAddr;
	this->port = port;
	this->d = d;
	debug("ipAddress: ", ipAddress);
	debug("port: ", port);
}

void Client::launchUI()
{
	string line;
	cout << "% ";
	while (getline(cin, line))
	{
		if (line.find("send") == 0)
		{
			debug("Send");
			sendMsg(&line);
		}
		else if(line.find("list") == 0)
		{
			debug("List");
			listMsg(&line);
		}
		else if(line.find("read") == 0)
		{
			debug("Read");
			readMsg(&line);
		}

		else if (line.find("quit") == 0)
		{
			debug("Shutting Down");
			break;
		}
		else
		{
			cout << "Command options: " << endl;
			cout << "\tsend [user] [subject]" << endl;
			cout << "\tlist [user]" << endl;
			cout << "\tread [user] [index]" << endl;
			cout << "\tquit" << endl;
		}
	
		cout << "% ";
	}
}

Client::~Client()
{
}

void Client::Client::connectServer()
{
	  // setup socket address structure
	memset(&server,0,sizeof(server));
	server.sin_family = AF_INET;
	server.sin_port = htons(port);
	
	if (!inet_aton(ipAddress ,&server.sin_addr)) {
		printf("inet_addr() conversion error\n");
		exit(-1);
	}
	debug("Creating Socket");
	  // create socket
	socket_handle = socket(PF_INET,SOCK_STREAM,0);
	if (socket_handle < 0) {
		perror("socket");
		exit(-1);
	}
	debug("Connecting to Server...");
	  // connect to server
	if (connect(socket_handle,(const struct sockaddr *)&server,sizeof(server)) < 0) {
		perror("connect");
		exit(-1);
	}
	debug("Connected to Server! Success!");
}

void Client::sendMsg(string * userInput)
{
	int firstArgIndex = userInput->find_first_of(" ");
	int secondArgIndex = userInput->find_first_of(" ", firstArgIndex+1);
	
	if(firstArgIndex < 0 || secondArgIndex < 0)
	{
		cout << "Usage: send [user] [subject]" << endl;
		return;
	}

	if (userInput->find_first_of(" ", secondArgIndex+1) != -1)
	{
		cout << "Unexpected white space: please ensure that your [user] and [subject] do not contain white space" << endl;
		return;
	}
	string user (userInput->substr(firstArgIndex+1, secondArgIndex - firstArgIndex -1));
	string subject (userInput->substr(secondArgIndex+1));
	debug("User: ", user);
	debug("Subject: ", subject);

	cout << "-- Type Your Message Below. End with a blank line --" << endl;;

	string message("");
	string line;
	while(getline(cin, line))
	{
		if(line.compare("") != 0)
		{
			message += "\n" + line;
		}
		else
		{
			break;
		}
	}
	string command ("put " + user + " " + subject + " ");
	command += static_cast<ostringstream*>( &(ostringstream() << message.length()-1) )->str();
	command += message;
	cout << "Sending..." << endl;;
	string response = sendCommand(&command);
	if (!response.empty())
	{
		if (response.compare("OK\n") == 0)
		{
			cout << "Status:OK\n" << endl;
		}
		else
		{
			cout << "Status:FAILED\n" << endl;
		}
	}
}

void Client::listMsg(string * userInput)
{
	int firstArgIndex = userInput->find_first_of(" ");

	if(firstArgIndex < 0)
	{
		cout << "Usage: list [user]" << endl;
		return;
	}
	
	if (userInput->find_first_of(" ", firstArgIndex+1) != -1)
	{
		cout << "Unexpected white space: please ensure that your [user] does not contain white space" << endl;
		return;
	}
	string user (userInput->substr(firstArgIndex+1));
	debug("User: ", user);

	string command ("list " + user+"\n");
	string response = sendCommand(&command);
	if (!response.empty())
	{
		handleListResponse(response);
	}
}

void Client::handleListResponse(string response)
{
	if (response.find_first_of("list") != 0)
	{
		debug("Bad list response");
		return;
	}
	else
	{
		response.erase(0,4);
	}

	if (response.find_first_of(" ") != 0)
	{
		debug("Bad list response");
		return;
	}
	else
		response.erase(0,1);
	
	int index = response.find_first_of('\n');

	int numMsgs = atoi(response.substr(0, index).c_str());
	response.erase(0, index+1);
	
	if (numMsgs == 0)
	{
		cout << "No messages found" << endl;
	}
	string list = response;
	debug("listing message indexes");
	for (int i = 0; i < numMsgs; i++)
	{
		index = response.find_first_of('\n');
		while (index == -1)
		{
			response += receiveResponse();
			index = response.find_first_of('\n');
		}
	}
	cout << response << endl;
	debug("ending list");
	
}

void Client::readMsg(string * userInput)
{
	int firstArgIndex = userInput->find_first_of(" ");
	int secondArgIndex = userInput->find_first_of(" ", firstArgIndex+1);
	
	if(firstArgIndex < 0 || secondArgIndex < 0)
	{
		cout << "Usage: read [user] [index]" << endl;
		return;
	}

	if (userInput->find_first_of(" ", secondArgIndex+1) != -1)
	{
		cout << "Unexpected white space: please ensure that your [user] and [index] do not contain white space" << endl;
		return;
	}
	string user (userInput->substr(firstArgIndex+1, secondArgIndex - firstArgIndex -1));
	string index (userInput->substr(secondArgIndex+1));
	debug("User: ", user);
	debug("Index: ", index);
	string command("get " + user + " " + index +"\n");
	string response = sendCommand(&command);
	if (!response.empty())
	{
		handleReadResponse(response);
	}
}

void Client::handleReadResponse(string response)
{
	if (response.find_first_of("message") != 0)
	{
		debug("Bad read response");
		return;
	}
	else
	{
		response.erase(0,7);
	}
	
	int index = response.find_first_of(" ");
	if (index != 0)
	{
		debug("Bad read response");
		return;
	}
	response.erase(0,1);

	index = response.find_first_of(" ");
	if (index == -1)
	{
		debug("Bad read response");
		return;
	}
	string user = response.substr(0, index);
	response.erase(0, index+1);

	index = response.find_first_of('\n');

	int length = atoi(response.substr(0, index).c_str());
	response.erase(0, index+1);

	const char * ptr = response.c_str();
	for (int i = 0; i < length; i++, ptr++)
	{
		while (ptr == '\0')
		{
			response += receiveResponse();
		}
	}
	cout << response << endl<< endl;
}

bool Client::isError(string * response)
{
	if (response->empty())
	{
		cout << "Status: Empty response from server - you have probably been disconnected" << endl;
		return true;
	}
	if (response->find_first_of("error") == 0)
	{
		cout << "Status: " << *response << endl;
		return true;
	}
	return false;
}

string Client::sendCommand(string * command)
{
	debug(command->c_str());
	const char *ptr = command->c_str();
	int nwritten, nleft;
	char *buf = (char *) calloc(1024,sizeof(char));
	nleft = command->length();
	debug("sending command:", *command);
	while (nleft) {
		if ((nwritten = send(socket_handle, ptr, nleft, 0)) < 0) {
		if (errno == EINTR) {
			  // the socket call was interrupted -- try again
			nwritten = 0;
		} else {
			  // an error occurred, so break out
			perror("write");
			break;
		}
		} else if (nwritten == 0) {
		  // the socket is closed
		break;
		}
		nleft -= nwritten;
		ptr += nwritten;
	}

	// needs to be smarter
	debug("start receiving");
	string response ("");
	do {
		char * buf = receiveResponse();
		if (buf == NULL)
		{
			debug("Error receiving response");
			break;
		}
		response += buf;
	} while (!containsNewLine(&response));
	debug(response.c_str());
	if (isError(&response))
	{
		return "";
	}

	debug("Partial response: ", response);
	return response;
}

bool Client::containsNewLine(string * str)
{
	debug("Find new line: ", str->find('\n')); 
	return (str->find('\n') != string::npos);
}

char * Client::receiveResponse()
{
	char * buf = new char[1024];
	memset(buf,0,1024);
	int nread = recv(socket_handle,buf,1024,0);
		if (nread < 0) {
			if (errno == EINTR)
		  // the socket call was interrupted -- try again
				buf = receiveResponse();
			else
		  // an error occurred, so break out
				return NULL;
		} else if (nread == 0) {
			  // the socket is closed
			return NULL;
		}
	return buf;
}

void Client::debug(char * statement)
{
	if(d)
	{
		cout << "[DEBUG] " << statement << endl;
	}
}

void Client::debug(const char * statement)
{
	if(d)
	{
		cout << "[DEBUG] " << statement << endl;
	}
}

void Client::debug(char * statement, char * variable)
{
	if(d)
	{
		cout << "[DEBUG] " << statement << variable << endl;
	}

}

void Client::debug(char * statement, const char * variable)
{
	if(d)
	{
		cout << "[DEBUG] " << statement << variable << endl;
	}

}

void Client::debug(char * statement, int variable)
{
	if(d)
	{
		cout << "[DEBUG] " << statement << variable << endl;
	}

}

void Client::debug(char * statement, string variable)
{
	if(d)
	{
		cout << "[DEBUG] " << statement << variable << endl;
	}

}
